
from dataclasses import dataclass, field
from enum import unique,Enum


@dataclass
class NodeLocation:
    file_path:str=""
    start_row:int=0
    start_column:int=0
    end_row:int=0
    end_column:int=0
    
    
    @property
    def key(self):
        return self.start_row*32768+self.start_column
    
    @staticmethod
    def parse(filepath:str,s:str):
        args=s.split(':')
        start_row,start_column,end_column,end_row=map(lambda x:int(x),args)
        
        return NodeLocation(start_row=start_row,
                            start_column=start_column,
                            end_row=end_row,
                            end_column=end_column,
                            file_path=filepath)

@unique
class ScopeType(Enum):
    Function=0
    Class=1
    Module=2
    Script=3
    Unknown=255
    
    @staticmethod
    def getScopeType(s:str):
        if s=="Function":
            return ScopeType.Function
        elif s=="Class":
            return ScopeType.Class
        elif s=='Module':
            return ScopeType.Module
        elif s=='Script':
            return ScopeType.Script
        else:
            return ScopeType.Unknown

@unique
class VarType(Enum):
    Global=0
    Local=1
    Unknown=255
    
    @staticmethod
    def getVarType(s:str):
        if s=="Global":
            return VarType.Global
        elif s=="Local":
            return VarType.Local
        else:
            return VarType.Unknown

@dataclass
class Scope:
    name:str=""
    scope_type:ScopeType=ScopeType.Unknown

    @staticmethod
    def parse(s:str):
        scope_type,name=s.split()
        return Scope(name=name,
        scope_type=ScopeType.getScopeType(scope_type))
        
@dataclass
class AstNode:
    node_type:str=""
    is_leaves_node:bool=False
    value:str=""
    
    scope:Scope=Scope()
    location:NodeLocation=NodeLocation()
    
    @property
    def text(self):
        return f'{self.node_type}:{self.value}'
    
    @property
    def id(self):
        return AstNode.id_construct(self.node_type,self.value,self.location)
    
    @staticmethod
    def id_construct(node_type,node_value,node_location:NodeLocation):
        location_str=f'{node_location.start_row},{node_location.start_column},{node_location.end_row},{node_location.end_column}'
        return f'{node_type}:{node_value}[{node_location.file_path}.{location_str}]'
    
    def __hash__(self) -> int:
        return self.id.__hash__()
    
@dataclass
class CfgNode:
    ast_node_id:str=""
    owner_block_id:str=""
    location:NodeLocation=NodeLocation()


    @property
    def id(self):
        return self.ast_node_id

    @staticmethod
    def id_construct(node_type,node_value,node_location:NodeLocation):
        return AstNode.id_construct(node_type,node_value,node_location)

class AstEdge:
    def __init__(self,start_id:str,end_id:str,file_path:str) -> None:
        self.start_id=start_id
        self.end_id=end_id
        self.file_path=file_path

class CfgEdge:
    def __init__(self,start_id:str,end_id:str,file_path:str) -> None:
        self.start_id=start_id
        self.end_id=end_id
        self.file_path=file_path

class TokenNode:
    def __init__(self,string,type,start,end) -> None:
        self.string=string
        self.type=type
        self.start=start
        self.end=end
        

@dataclass
class BasicBlockInfo:
    scope:Scope=Scope()
    location:NodeLocation=NodeLocation()
    last_node_id:str=""

    @property
    def id(self):
        return self.id_construct(self.last_node_id,self.block_location)

    @staticmethod
    def id_construct(last_node_id:str,block_location:NodeLocation):
        location_str=f'{block_location.start_row},{block_location.start_column},{block_location.end_row},{block_location.end_column}'
        return f"{last_node_id}.{location_str}"

class BasicBlockEdge:
    def __init__(self,origin_id:str,successor_id:str,file_path:str) -> None:
        self.origin_id=origin_id
        self.successor_id=successor_id
        self.file_path=file_path
        
@dataclass
class VariableLocation:
    _absolute_path:int=0
    scope_names:list=field(default_factory=list)
    scope_types:list=field(default_factory=list)
    
    @property
    def module_name(self):
        return self.scope_names[0]